﻿using System.Security.Cryptography;
using System.Text;

namespace Sage.Data.Extensions
{
    /// <summary>
    /// 提供字节数组类型的扩展方法，用于编码转换、哈希计算等常用操作。
    /// </summary>
    /// <remarks>
    /// 此类包含针对byte[]类型的扩展方法，使字节数组相关操作更加简洁和直观。
    /// 所有方法都是线程安全的，可以在多线程环境中使用。
    /// </remarks>
    public static class ByteExtensions
    {
        /// <summary>
        /// 将字节数组转换为 Base64 编码的字符串。
        /// </summary>
        /// <param name="bytes">要编码的字节数组。</param>
        /// <returns>Base64 编码的字符串，如果输入为 null 或空数组则返回空字符串。</returns>
        /// <example>
        /// <code>
        /// byte[] data = Encoding.UTF8.GetBytes("Hello World");
        /// string base64 = data.ToBase64String(); // 返回 "SGVsbG8gV29ybGQ="
        /// 
        /// byte[] empty = [];
        /// string result = empty.ToBase64String(); // 返回 ""
        /// 
        /// byte[] nullArray = null;
        /// string result2 = nullArray.ToBase64String(); // 返回 ""
        /// </code>
        /// </example>
        /// <remarks>
        /// 适用于文件上传、API数据传输、数据存储等需要将二进制数据转换为文本格式的场景。
        /// </remarks>
        public static string ToBase64String(this byte[]? bytes)
        {
            if (bytes == null || bytes.Length == 0)
                return string.Empty;

            return Convert.ToBase64String(bytes);
        }

        /// <summary>
        /// 将字节数组转换为十六进制字符串（大写）。
        /// </summary>
        /// <param name="bytes">要转换的字节数组。</param>
        /// <returns>十六进制编码的字符串（大写），如果输入为 null 或空数组则返回空字符串。</returns>
        /// <example>
        /// <code>
        /// byte[] data = { 0xFF, 0x00, 0xAB };
        /// string hex = data.ToHexString(); // 返回 "FF00AB"
        /// 
        /// byte[] hash = MD5.HashData(Encoding.UTF8.GetBytes("test"));
        /// string hashHex = hash.ToHexString(); // 返回哈希的十六进制表示
        /// </code>
        /// </example>
        /// <remarks>
        /// 常用于显示文件哈希值、调试二进制数据、生成唯一标识符等场景。
        /// </remarks>
        public static string ToHexString(this byte[]? bytes)
        {
            if (bytes == null || bytes.Length == 0)
                return string.Empty;

            return Convert.ToHexString(bytes);
        }

        /// <summary>
        /// 将字节数组转换为小写十六进制字符串。
        /// </summary>
        /// <param name="bytes">要转换的字节数组。</param>
        /// <returns>小写十六进制编码的字符串，如果输入为 null 或空数组则返回空字符串。</returns>
        /// <example>
        /// <code>
        /// byte[] data = { 0xFF, 0x00, 0xAB };
        /// string hex = data.ToHexStringLower(); // 返回 "ff00ab"
        /// </code>
        /// </example>
        public static string ToHexStringLower(this byte[]? bytes)
        {
            return bytes.ToHexString().ToLowerInvariant();
        }

        /// <summary>
        /// 将字节数组转换为HMAC-SHA1哈希值
        /// </summary>
        /// <param name="bytes">要进行哈希计算的字节数组</param>
        /// <param name="key">HMAC密钥字符串</param>
        /// <returns>计算得到的HMAC-SHA1哈希值字节数组</returns>
        public static byte[] ToHmacSha1(this byte[] bytes, string key)
        {
            using var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(key));
            return hmac.ComputeHash(bytes);
        }

        /// <summary>
        /// 将字节数组转换为URL安全的Base64编码字符串。
        /// </summary>
        /// <param name="bytes">要编码的字节数组。</param>
        /// <returns>
        /// URL安全的Base64编码字符串，其中'+'替换为'-'，'/'替换为'_'，并移除末尾的'='填充字符。
        /// </returns>
        public static string ToUrlSafeBase64(this byte[] bytes)
        {
            var base64 = Convert.ToBase64String(bytes);
            return base64.Replace("+", "-").Replace("/", "_").TrimEnd('=');
        }

        /// <summary>
        /// 将字节数组转换为UTF-8编码的字符串。
        /// </summary>
        /// <param name="bytes">要转换的字节数组。</param>
        /// <returns>UTF-8解码后的字符串，如果输入为 null 或空数组则返回空字符串。</returns>
        /// <example>
        /// <code>
        /// byte[] utf8Bytes = Encoding.UTF8.GetBytes("Hello 世界");
        /// string text = utf8Bytes.ToUtf8String(); // 返回 "Hello 世界"
        /// 
        /// // 从文件读取的字节数组转换为文本
        /// byte[] fileBytes = File.ReadAllBytes("text.txt");
        /// string content = fileBytes.ToUtf8String();
        /// </code>
        /// </example>
        /// <remarks>
        /// 适用于处理文本文件、API响应、网络数据等UTF-8编码的字节数据。
        /// 如果字节数组不是有效的UTF-8编码，可能会出现乱码或异常。
        /// </remarks>
        public static string ToUtf8String(this byte[]? bytes)
        {
            if (bytes == null || bytes.Length == 0)
                return string.Empty;

            return Encoding.UTF8.GetString(bytes);
        }

        /// <summary>
        /// 使用指定编码将字节数组转换为字符串。
        /// </summary>
        /// <param name="bytes">要转换的字节数组。</param>
        /// <param name="encoding">字符编码，默认为UTF-8。</param>
        /// <returns>解码后的字符串，如果输入为 null 或空数组则返回空字符串。</returns>
        /// <example>
        /// <code>
        /// byte[] gbkBytes = Encoding.GetEncoding("GBK").GetBytes("中文测试");
        /// string text = gbkBytes.ToStringWithEncoding(Encoding.GetEncoding("GBK")); // 正确解码中文
        /// </code>
        /// </example>
        public static string ToStringWithEncoding(this byte[]? bytes, Encoding? encoding = null)
        {
            if (bytes == null || bytes.Length == 0)
                return string.Empty;

            encoding ??= Encoding.UTF8;
            return encoding.GetString(bytes);
        }

        /// <summary>
        /// 计算字节数组的MD5哈希值。
        /// </summary>
        /// <param name="bytes">要计算哈希的字节数组。</param>
        /// <returns>MD5哈希值的字节数组，如果输入为 null 或空数组则返回空数组。</returns>
        /// <example>
        /// <code>
        /// byte[] data = Encoding.UTF8.GetBytes("Hello World");
        /// byte[] hash = data.ToMD5(); 
        /// string hashString = hash.ToHexStringLower(); // MD5哈希的十六进制表示
        /// 
        /// // 文件完整性校验
        /// byte[] fileBytes = File.ReadAllBytes("file.txt");
        /// string fileMD5 = fileBytes.ToMD5().ToHexStringLower();
        /// </code>
        /// </example>
        /// <remarks>
        /// MD5算法已被认为在密码学上不安全，不建议用于安全敏感的场景。
        /// 适用于文件完整性校验、数据去重等非安全相关的场景。
        /// </remarks>
        public static byte[] ToMD5yte(this byte[]? bytes)
        {
            if (bytes == null || bytes.Length == 0)
                return [];

            return MD5.HashData(bytes);
        }

        /// <summary>
        /// 计算字节数组的SHA256哈希值。
        /// </summary>
        /// <param name="bytes">要计算哈希的字节数组。</param>
        /// <returns>SHA256哈希值的字节数组，如果输入为 null 或空数组则返回空数组。</returns>
        /// <example>
        /// <code>
        /// byte[] data = Encoding.UTF8.GetBytes("Hello World");
        /// byte[] hash = data.ToSHA256();
        /// string hashString = hash.ToHexStringLower(); // SHA256哈希的十六进制表示
        /// </code>
        /// </example>
        /// <remarks>
        /// SHA256是目前推荐的安全哈希算法，适用于密码存储、数字签名等安全场景。
        /// </remarks>
        public static byte[] ToSHA256(this byte[]? bytes)
        {
            if (bytes == null || bytes.Length == 0)
                return [];

            return SHA256.HashData(bytes);
        }

        /// <summary>
        /// 比较两个字节数组是否相等。
        /// </summary>
        /// <param name="bytes1">要比较的第一个字节数组。</param>
        /// <param name="bytes2">要比较的第二个字节数组。</param>
        /// <returns>如果两个数组内容相同则返回true，否则返回false。</returns>
        /// <example>
        /// <code>
        /// byte[] data1 = { 1, 2, 3 };
        /// byte[] data2 = { 1, 2, 3 };
        /// byte[] data3 = { 1, 2, 4 };
        /// 
        /// bool isEqual1 = data1.SequenceEqual(data2); // 返回 true
        /// bool isEqual2 = data1.SequenceEqual(data3); // 返回 false
        /// </code>
        /// </example>
        public static bool SequenceEqual(this byte[]? bytes1, byte[]? bytes2)
        {
            if (bytes1 == null && bytes2 == null) return true;
            if (bytes1 == null || bytes2 == null) return false;

            return bytes1.AsSpan().SequenceEqual(bytes2.AsSpan());
        }

        /// <summary>
        /// 获取字节数组的易读可读大小格式。
        /// </summary>
        /// <param name="bytes">字节数组。</param>
        /// <returns>格式化后的大小字符串。</returns>
        /// <example>
        /// <code>
        /// byte[] data = new byte[1024];
        /// string size = data.FormatSize(); // 返回 "1.00 KB"
        /// </code>
        /// </example>
        public static string FormatSize(this byte[]? bytes)
        {
            if (bytes == null) return "0 B";
            return ((long)bytes.Length).ToFileSizeString();
        }
    }
}
